#ifndef SWFStruct_h__
#define SWFStruct_h__

#include "SWFTypes.h"
#include <string>
#include <vector>
#include <assert.h>

class DefineTag;
class SWFInputStream;
class SWFOutputStream;
class ShapeWithStyle;

/* Type for color record. */
class ColorRGB
{
public:
	UI8 Red;
	UI8 Green;
	UI8 Blue;
public:
	ColorRGB()
		: Red(0)
		, Green(0)
		, Blue(0)
	{

	}
};

class ColorRGBA : public ColorRGB
{
public:
	UI8 Alpha;
public:
	ColorRGBA()
		: Alpha(1)
	{

	}
};

class ARGB : public ColorRGBA
{
};

class Matrix
{
public:
	UB HasScale;//UB 1
	UB NScaleBits;// If HasScale = 1, UB[5] Bits in each scale value field
	FB ScaleX;// If HasScale = 1, FB[NScaleBits]  x scale value
	FB ScaleY;// If HasScale = 1, FB[NScaleBits]  y scale value
	UB HasRotate;// UB[1] Has rotate and skew values if equal to 1
	UB NRotateBits;// If HasRotate = 1, UB[5] Bits in each rotate value field
	FB RotateSkew0;// If HasRotate = 1, FB[NRotateBits]  First rotate and skew value
	FB RotateSkew1;// If HasRotate = 1, FB[NRotateBits]  Second rotate and skew value
	UB NTranslateBits;// UB[5] Bits in each translate value field
	SB TranslateX;// SB[NTranslateBits] x translate value in twips
	SB TranslateY;//  SB[NTranslateBits] y translate value in twips
public:
	Matrix()
		: HasScale(0)
		, NScaleBits(0)
		, ScaleX(0)
		, ScaleY(0)
		, HasRotate(0)
		, NRotateBits(0)
		, RotateSkew0(0)
		, RotateSkew1(0)
		, NTranslateBits(0)
		, TranslateX(0)
		, TranslateY(0)
	{

	}
};


class CXFORM
{
public:
	UB HasAddTerms;//  UB[1]  Has color addition values if equal to 1
	UB HasMultTerms;// UB[1]  Has color multiply values if equal to 1
	UB Nbits;//  UB[4]  Bits in each value field
	SB RedMultTerm;//  If HasMultTerms = 1, SB[Nbits]  Red multiply value
	SB GreenMultTerm;//  If HasMultTerms = 1, SB[Nbits]  Green multiply value
	SB BlueMultTerm;//  If HasMultTerms = 1, SB[Nbits]  Blue multiply value
	SB RedAddTerm;//  If HasAddTerms = 1, SB[Nbits] Red addition value
	SB GreenAddTerm;//  If HasAddTerms = 1, SB[Nbits] Green addition value
	SB BlueAddTerm;// If HasAddTerms = 1, SB[Nbits]  Blue addition value
public:
	CXFORM()
		: HasAddTerms(0)
		, HasMultTerms(0)
		, Nbits(0)
		, RedMultTerm(0)
		, GreenMultTerm(0)
		, BlueMultTerm(0)
		, RedAddTerm(0)
		, GreenAddTerm(0)
		, BlueAddTerm(0)
	{

	}
};

class CXFORMWITHALPHA : public CXFORM
{
public:
	SB AlphaMultTerm;//  If HasMultTerms = 1, SB[Nbits]  Alpha multiply value
	SB AlphaAddTerm;//  If HasAddTerms = 1, SB[Nbits]  Transparency addition value
public:
	CXFORMWITHALPHA()
		: AlphaMultTerm(0)
		, AlphaAddTerm(0)
	{

	}
};

/* Type for Rectangle record. */
class Rect
{
public:
	UI32 NBits;
	SB Xmin;
	SB Xmax;
	SB Ymin;
	SB Ymax;
public:
	Rect()
		: NBits(0)
		, Xmin(0)
		, Xmax(0)
		, Ymin(0)
		, Ymax(0)
	{

	}
};

/* File structure */
typedef struct
{
	UI8 Signature[3];
	UI8 Version;
	UI32 FileLength;
	Rect FrameSize;
	UI16 FrameRate;
	UI16 FrameCount;
} SWFHeader;

class Filter
{

};

class GradRecord
{
public:
	UI8 Ratio;
	// RGB (Shape1 or Shape2)
	// RGBA (Shape3)
	ColorRGB Color;
};

class Gradient
{
public:
	//UB[2]
	UB SpreadMode;
	//UB[2]
	UB InterpolationMode;
	//UB[4]
	UB NumGradients;

	std::vector<GradRecord*> GradientRecords;
public:
	virtual void encode(SWFOutputStream* stream, UI8 tagCode) const;
};

class FocalGradient
	: public Gradient
{
public:
	FIXED8 FocalPoint;
public:
	void encode(SWFOutputStream* stream, UI8 tagCode) const override;
};

class FillStyle
{
public:
	// Type of fill style:
	// 0x00 = solid fill
	// 0x10 = linear gradient fill
	// 0x12 = radial gradient fill
	// 0x13 = focal radial gradient fill (SWF8 file format and later only)
	// 0x40 = repeating bitmap fill
	// 0x41 = clipped bitmap fill
	// 0x42 = non-smoothed repeating bitmap
	// 0x43 = non-smoothed clipped bitmap
	UI8 FillStyleType;
	// If type = 0x00
	// RGBA (if Shape3);
	// RGB (if Shape1 or Shape2)
	ColorRGB Color;
	// If type = 0x10, 0x12, or 0x13
	Matrix GradientMatrix;
	// If type = 0x10 or 0x12, GRADIENT 
	// If type = 0x13, FOCALGRADIENT (SWF
	// 8 and later only)
	Gradient* Gradient;
	// If type = 0x40, 0x41, 0x42 or 0x43
	UI16 BitmapId;
	DefineTag* Bitmap;
	// If type = 0x40, 0x41, 0x42 or 0x43
	Matrix BitmapMatrix;
public:
	FillStyle()
		: Bitmap(nullptr)
		, Gradient(nullptr)
		, FillStyleType(0)
		, BitmapId(0xffff)
	{

	}
public:
	void encode(SWFOutputStream* stream, UI8 tagCode) const;
};

class FillStyleArray
{
public:
	// If FillStyleCount = 0xFF
	UI16 FillStyleCountExtended;
	std::vector<FillStyle*> FillStyles;
public:
	void encode(SWFOutputStream* stream, UI8 tagCode) const;
public:
	FillStyleArray()
		: FillStyleCountExtended(0)
	{

	}
};

class LineStyle
{
public:
	UI16 Width;
	// RGB (Shape1 or Shape2)
	// RGBA (Shape3)
	ColorRGB Color;
public:
	virtual void encode(SWFOutputStream* stream, UI8 tagCode) const;
	LineStyle()
		: Width(0)
	{

	}
};

class LineStyle2
	: public LineStyle
{
public:
	// UB[2]
	UB StartCapStyle;
	// UB[2]
	UB JoinStyle;
	// UB[1]
	UB HasFillFlag;
	// UB[1]
	UB NoHScaleFlag;
	// UB[1]
	UB NoVScaleFlag;
	// UB[1]
	UB PixelHintingFlag;
	// UB[5]
	UB Reserved;
	// UB[1]
	UB NoClose;
	// UB[2]
	UB EndCapStyle;
	//If JoinStyle = 2
	UI16 MiterLimitFactor;
	// If HasFillFlag = 1
	FillStyle* FillType;
public:
	LineStyle2()
		: FillType(nullptr)
		, StartCapStyle(0)
		, JoinStyle(0)
		, HasFillFlag(0)
		, NoHScaleFlag(0)
		, NoVScaleFlag(0)
		, PixelHintingFlag(0)
		, Reserved(0)
		, NoClose(0)
		, EndCapStyle(0)
		, MiterLimitFactor(0)
	{

	}
	void encode(SWFOutputStream* stream, UI8 tagCode) const override;
};

class LineStyleArray
{
public:
	// If LineStyleCount = 0xFF, UI16 
	UI16 LineStyleCountExtended;
	std::vector<LineStyle*> LineStyles;
public:
	LineStyleArray()
		: LineStyleCountExtended(0)
	{

	}
	void encode(SWFOutputStream* stream, UI8 tagCode) const;
};

#pragma region Shape Records
class ShapeRecord
{
public:
	virtual void encode(SWFOutputStream* stream, UI8 tagCode, ShapeWithStyle* style) const = 0;
};

class EndShapeRecord
	: public ShapeRecord
{
public:
	// UB[1]
	UB TypeFlag;
	// UB[5]
	UB EndOfShape;
public:
	EndShapeRecord()
		: TypeFlag(0)
		, EndOfShape(0)
	{

	}
	void encode(SWFOutputStream* stream, UI8 tagCode, ShapeWithStyle* style) const override;
};

class StyleChangeRecord
	: public ShapeRecord
{
public:
	// UB[1]  Non-edge record flag. Always 0.
	UB TypeFlag;
	// UB[1]  New styles flag. Used by
	// DefineShape2 and DefineShape3
	// only.
	UB StateNewStyles;
	// UB[1]  Line style change flag.
	UB StateLineStyle;
	// UB[1]  Fill style 1 change flag.
	UB StateFillStyle1;
	// UB[1]  Fill style 0 change flag.
	UB StateFillStyle0;
	// UB[1]  Move to flag.
	UB StateMoveTo;
	// If StateMoveTo, UB[5] Move bit count.
	UB MoveBits;
	// If StateMoveTo, SB[MoveBits] Delta X value.
	UB MoveDeltaX;
	// If StateMoveTo, SB[MoveBits] Delta Y value.
	UB MoveDeltaY;
	// If StateFillStyle0, UB[FillBits] Fill 0 Style.
	UB FillStyle0;
	// If StateFillStyle1, UB[FillBits] Fill 1 Style.
	UB FillStyle1;
	// If StateLineStyle, UB[LineBits] Line Style.
	UB LineStyle;
	// If StateNewStyles, FILLSTYLEARRAY  Array of new fill styles.
	FillStyleArray* FillStyles;
	// If StateNewStyles, LINESTYLEARRAY  Array of new line styles.
	LineStyleArray* LineStyles;
	// If StateNewStyles, UB[4] Number of fill index bits for new
	// styles.
	UB NumFillBits;
	// If StateNewStyles, UB[4] Number of line index bits for new
	// styles.
	UB NumLineBits;
public:
	StyleChangeRecord()
		: FillStyles(nullptr)
		, LineStyles(nullptr)
		, TypeFlag(0)
		, StateNewStyles(0)
		, StateLineStyle(0)
		, StateFillStyle1(0)
		, StateFillStyle0(0)
		, StateMoveTo(0)
		, MoveBits(0)
		, MoveDeltaX(0)
		, MoveDeltaY(0)
		, FillStyle0(0)
		, FillStyle1(0)
		, LineStyle(0)
		, NumFillBits(0)
		, NumLineBits(0)
	{

	}
	void encode(SWFOutputStream* stream, UI8 tagCode, ShapeWithStyle* style) const override;
};

class StraightEdgeRecord
	: public ShapeRecord
{
public:
	// UB[1] This is an edge record. Always 1.
	UB TypeFlag;
	// UB[1] Straight edge. Always 1.
	UB StraightFlag;
	// UB[4] Number of bits per value (2 less than the actual number).
	UB NumBits;
	// UB[1] General Line equals 1. Vert/Horz Line equals 0.
	UB GeneralLineFlag;
	// If GeneralLineFlag = 0, SB[1]  Vertical Line equals 1. Horizontal Line equals 0.
	UB VertLineFlag;
	// If GeneralLineFlag = 1 or if VertLineFlag = 0, SB[NumBits+2]
	// X delta
	UB DeltaX;
	// If GeneralLineFlag = 1 or if VertLineFlag = 1, SB[NumBits+2]
	// Y delta.
	UB DeltaY;
public:
	StraightEdgeRecord()
		: TypeFlag(1)
		, StraightFlag(1)
		, NumBits(0)
		, GeneralLineFlag(0)
		, VertLineFlag(0)
		, DeltaX(0)
		, DeltaY(0)
	{

	}
	void encode(SWFOutputStream* stream, UI8 tagCode, ShapeWithStyle* style) const override;
};

class CurvedEdgeRecord
	: public ShapeRecord
{
public:
	// UB[1]  This is an edge record. Always 1.
	UB TypeFlag;
	// UB[1]  Curved edge. Always 0.
	UB StraightFlag;
	// UB[4]  Number of bits per value (2 less than the actual number).
	UB NumBits;
	// SB[NumBits+2]  X control point change.
	SB ControlDeltaX;
	// SB[NumBits+2]  Y control point change.
	SB ControlDeltaY;
	// SB[NumBits+2]  X anchor point change.
	SB AnchorDeltaX;
	// SB[NumBits+2]  Y anchor point change.
	SB AnchorDeltaY;
public:
	CurvedEdgeRecord()
		: TypeFlag(1)
		, StraightFlag(0)
		, NumBits(0)
		, ControlDeltaX(0)
		, ControlDeltaY(0)
		, AnchorDeltaX(0)
		, AnchorDeltaY(0)
	{

	}
	void encode(SWFOutputStream* stream, UI8 tagCode, ShapeWithStyle* style) const override;
};
#pragma endregion Shape Records

class ShapeWithStyle
{
public:
	FillStyleArray* FillStyles;
	LineStyleArray* LineStyles;
	// UB[4]
	UB NumFillBits;
	// UB[4]
	UB NumLineBits;
	// ShapeRecords
	std::vector<ShapeRecord*> ShapeRecords;
public:
	ShapeWithStyle()
		: FillStyles(nullptr)
		, LineStyles(nullptr)
		, NumFillBits(0)
		, NumLineBits(0)
	{

	}
};

#endif // SWFTag_h__
